/*++

Copyright (c) 2004 - 2005, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  Keyboard.c
    
Abstract:

  Helper functions for USB Keyboard Driver

Revision History

--*/

#include "KeyBoard.h"
#include "Hid.h"

//
// USB Key Code to Efi key mapping table
// Format:<efi scan code>, <unicode without shift>, <unicode with shift>
//
STATIC
UINT8 KeyConvertionTable[USB_KEYCODE_MAX_MAKE][3] = {
    SCAN_NULL,      'a',      'A',      // 0x04
    SCAN_NULL,      'b',      'B',      // 0x05
    SCAN_NULL,      'c',      'C',      // 0x06
    SCAN_NULL,      'd',      'D',      // 0x07
    SCAN_NULL,      'e',      'E',      // 0x08
    SCAN_NULL,      'f',      'F',      // 0x09
    SCAN_NULL,      'g',      'G',      // 0x0A
    SCAN_NULL,      'h',      'H',      // 0x0B
    SCAN_NULL,      'i',      'I',      // 0x0C
    SCAN_NULL,      'j',      'J',      // 0x0D
    SCAN_NULL,      'k',      'K',      // 0x0E
    SCAN_NULL,      'l',      'L',      // 0x0F
    SCAN_NULL,      'm',      'M',      // 0x10
    SCAN_NULL,      'n',      'N',      // 0x11
    SCAN_NULL,      'o',      'O',      // 0x12
    SCAN_NULL,      'p',      'P',      // 0x13
    SCAN_NULL,      'q',      'Q',      // 0x14
    SCAN_NULL,      'r',      'R',      // 0x15
    SCAN_NULL,      's',      'S',      // 0x16
    SCAN_NULL,      't',      'T',      // 0x17
    SCAN_NULL,      'u',      'U',      // 0x18
    SCAN_NULL,      'v',      'V',      // 0x19
    SCAN_NULL,      'w',      'W',      // 0x1A
    SCAN_NULL,      'x',      'X',      // 0x1B
    SCAN_NULL,      'y',      'Y',      // 0x1C
    SCAN_NULL,      'z',      'Z',      // 0x1D
    SCAN_NULL,      '1',      '!',      // 0x1E
    SCAN_NULL,      '2',      '@',      // 0x1F
    SCAN_NULL,      '3',      '#',      // 0x20
    SCAN_NULL,      '4',      '$',      // 0x21
    SCAN_NULL,      '5',      '%',      // 0x22
    SCAN_NULL,      '6',      '^',      // 0x23
    SCAN_NULL,      '7',      '&',      // 0x24
    SCAN_NULL,      '8',      '*',      // 0x25
    SCAN_NULL,      '9',      '(',      // 0x26
    SCAN_NULL,      '0',      ')',      // 0x27
    SCAN_NULL,      0x0d,     0x0d,     // 0x28   Enter
    SCAN_ESC,       0x00,     0x00,     // 0x29   Esc
    SCAN_NULL,      0x08,     0x08,     // 0x2A   Backspace
    SCAN_NULL,      0x09,     0x09,     // 0x2B   Tab
    SCAN_NULL,      ' ',      ' ',      // 0x2C   Spacebar
    SCAN_NULL,      '-',      '_',      // 0x2D
    SCAN_NULL,      '=',      '+',      // 0x2E
    SCAN_NULL,      '[',      '{',      // 0x2F
    SCAN_NULL,      ']',      '}',      // 0x30
    SCAN_NULL,      '\\',     '|',      // 0x31
    SCAN_NULL,      '\\',     '|',      // 0x32  Keyboard US \ and |
    SCAN_NULL,      ';',      ':',      // 0x33
    SCAN_NULL,      '\'',     '"',      // 0x34
    SCAN_NULL,      '`',      '~',      // 0x35  Keyboard Grave Accent and Tlide
    SCAN_NULL,      ',',      '<',      // 0x36
    SCAN_NULL,      '.',      '>',      // 0x37
    SCAN_NULL,      '/',      '?',      // 0x38
    SCAN_NULL,      0x00,     0x00,     // 0x39   CapsLock
    SCAN_F1,        0x00,     0x00,     // 0x3A
    SCAN_F2,        0x00,     0x00,     // 0x3B
    SCAN_F3,        0x00,     0x00,     // 0x3C  
    SCAN_F4,        0x00,     0x00,     // 0x3D  
    SCAN_F5,        0x00,     0x00,     // 0x3E
    SCAN_F6,        0x00,     0x00,     // 0x3F
    SCAN_F7,        0x00,     0x00,     // 0x40
    SCAN_F8,        0x00,     0x00,     // 0x41
    SCAN_F9,        0x00,     0x00,     // 0x42
    SCAN_F10,       0x00,     0x00,     // 0x43
    SCAN_F11,       0x00,     0x00,     // 0x44   F11
    SCAN_F12,       0x00,     0x00,     // 0x45   F12
    SCAN_NULL,      0x00,     0x00,     // 0x46   PrintScreen
    SCAN_NULL,      0x00,     0x00,     // 0x47   Scroll Lock
    SCAN_NULL,      0x00,     0x00,     // 0x48   Pause
    SCAN_INSERT,    0x00,     0x00,     // 0x49
    SCAN_HOME,      0x00,     0x00,     // 0x4A
    SCAN_PAGE_UP,   0x00,     0x00,     // 0x4B
    SCAN_DELETE,    0x00,     0x00,     // 0x4C
    SCAN_END,       0x00,     0x00,     // 0x4D
    SCAN_PAGE_DOWN, 0x00,     0x00,     // 0x4E
    SCAN_RIGHT,     0x00,     0x00,     // 0x4F
    SCAN_LEFT,      0x00,     0x00,     // 0x50
    SCAN_DOWN,      0x00,     0x00,     // 0x51
    SCAN_UP,        0x00,     0x00,     // 0x52
    SCAN_NULL,      0x00,     0x00,     // 0x53   NumLock
    SCAN_NULL,      '/',      '/',      // 0x54
    SCAN_NULL,      '*',      '*',      // 0x55
    SCAN_NULL,      '-',      '-',      // 0x56
    SCAN_NULL,      '+',      '+',      // 0x57
    SCAN_NULL,      0x0d,     0x0d,     // 0x58
    SCAN_END,       '1',      '1',      // 0x59
    SCAN_DOWN,      '2',      '2',      // 0x5A
    SCAN_PAGE_DOWN, '3',      '3',      // 0x5B
    SCAN_LEFT,      '4',      '4',      // 0x5C
    SCAN_NULL,      '5',      '5',      // 0x5D
    SCAN_RIGHT,     '6',      '6',      // 0x5E
    SCAN_HOME,      '7',      '7',      // 0x5F
    SCAN_UP,        '8',      '8',      // 0x60
    SCAN_PAGE_UP,   '9',      '9',      // 0x61
    SCAN_INSERT,    '0',      '0',      // 0x62
    SCAN_DELETE,    '.',      '.',      // 0x63
    SCAN_NULL,      '\\',     '|',      // 0x64 Keyboard Non-US \ and |
    SCAN_NULL,      0x00,     0x00,     // 0x65 Keyboard Application
    SCAN_NULL,      0x00,     0x00,     // 0x66 Keyboard Power
    SCAN_NULL,      '=' ,     '='       // 0x67 Keypad =
};

STATIC KB_MODIFIER  KB_Mod[8] = {
  { MOD_CONTROL_L,  0xe0 }, // 11100000 
  { MOD_CONTROL_R,  0xe4 }, // 11100100 
  { MOD_SHIFT_L,    0xe1 }, // 11100001 
  { MOD_SHIFT_R,    0xe5 }, // 11100101 
  { MOD_ALT_L,      0xe2 }, // 11100010 
  { MOD_ALT_R,      0xe6 }, // 11100110 
  { MOD_WIN_L,      0xe3 }, // 11100011 
  { MOD_WIN_R,      0xe7 }, // 11100111 
};


BOOLEAN
IsUSBKeyboard (
  IN  EFI_USB_IO_PROTOCOL       *UsbIo
  )
/*++
  
  Routine Description:
    Uses USB I/O to check whether the device is a USB Keyboard device.
  
  Arguments:
    UsbIo:    Points to a USB I/O protocol instance.
    
  Returns:
  
--*/  
{
  EFI_STATUS                    Status;
  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

  //
  // Get the Default interface descriptor, currently we
  // assume it is interface 1
  //
  Status = UsbIo->UsbGetInterfaceDescriptor (
                    UsbIo,
                    &InterfaceDescriptor
                    );

  if (EFI_ERROR (Status)) {
    return FALSE;
  }

  if (InterfaceDescriptor.InterfaceClass == CLASS_HID &&
      InterfaceDescriptor.InterfaceSubClass == SUBCLASS_BOOT &&
      InterfaceDescriptor.InterfaceProtocol == PROTOCOL_KEYBOARD
      ) {

    return TRUE;
  }

  return FALSE;
}


EFI_STATUS
InitUSBKeyboard (
  IN USB_KB_DEV   *UsbKeyboardDevice
  )
/*++
  
  Routine Description:
    Initialize USB Keyboard device and all private data structures.
    
  Arguments:
    UsbKeyboardDevice    The USB_KB_DEV instance.
    
  Returns:
    EFI_SUCCESS      - Success
    EFI_DEVICE_ERROR - Hardware Error
--*/
{
  UINT8               ConfigValue;
  UINT8               Protocol;
  UINT8               ReportId;
  UINT8               Duration;
  EFI_STATUS          Status;
  UINT32              TransferResult;
  EFI_USB_IO_PROTOCOL *UsbIo;

  UsbIo = UsbKeyboardDevice->UsbIo;

  KbdReportStatusCode (
    UsbKeyboardDevice->DevicePath,
    EFI_PROGRESS_CODE,
    (EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_PC_SELF_TEST)
    );

  InitUSBKeyBuffer (&(UsbKeyboardDevice->KeyboardBuffer));

  //
  // default configurations
  //
  ConfigValue = 0x01;

  //
  // Uses default configuration to configure the USB Keyboard device.
  //
  Status = UsbSetDeviceConfiguration (
            UsbKeyboardDevice->UsbIo,
            (UINT16) ConfigValue,
            &TransferResult
            );
  if (EFI_ERROR (Status)) {
    //
    // If configuration could not be set here, it means
    // the keyboard interface has some errors and could
    // not be initialized
    //
    KbdReportStatusCode (
      UsbKeyboardDevice->DevicePath,
      EFI_ERROR_CODE | EFI_ERROR_MINOR,
      (EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_INTERFACE_ERROR)
      );

    return EFI_DEVICE_ERROR;
  }

  UsbGetProtocolRequest (
    UsbKeyboardDevice->UsbIo,
    UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
    &Protocol
    );
  //
  // Sets boot protocol for the USB Keyboard.
  // This driver only supports boot protocol.
  // !!BugBug: How about the device that does not support boot protocol?
  //
  if (Protocol != BOOT_PROTOCOL) {
    UsbSetProtocolRequest (
      UsbKeyboardDevice->UsbIo,
      UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
      BOOT_PROTOCOL
      );
  }
  //
  // the duration is indefinite, so the endpoint will inhibit reporting forever,
  // and only reporting when a change is detected in the report data.
  //

  //
  // idle value for all report ID
  //
  ReportId = 0;
  //
  // idle forever until there is a key pressed and released.
  //
  Duration = 0;
  UsbSetIdleRequest (
    UsbKeyboardDevice->UsbIo,
    UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
    ReportId,
    Duration
    );

  UsbKeyboardDevice->CtrlOn     = 0;
  UsbKeyboardDevice->AltOn      = 0;
  UsbKeyboardDevice->ShiftOn    = 0;
  UsbKeyboardDevice->NumLockOn  = 0;
  UsbKeyboardDevice->CapsOn     = 0;
  UsbKeyboardDevice->ScrollOn   = 0;
  EfiZeroMem (UsbKeyboardDevice->LastKeyCodeArray, sizeof (UINT8) * 8);

  //
  // Set a timer for repeat keys' generation.
  //
  if (UsbKeyboardDevice->RepeatTimer) {
    gBS->CloseEvent (UsbKeyboardDevice->RepeatTimer);
    UsbKeyboardDevice->RepeatTimer = 0;
  }

  Status = gBS->CreateEvent (
                  EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
                  EFI_TPL_NOTIFY,
                  USBKeyboardRepeatHandler,
                  UsbKeyboardDevice,
                  &UsbKeyboardDevice->RepeatTimer
                  );

  if (UsbKeyboardDevice->DelayedRecoveryEvent) {
    gBS->CloseEvent (UsbKeyboardDevice->DelayedRecoveryEvent);
    UsbKeyboardDevice->DelayedRecoveryEvent = 0;
  }

  Status = gBS->CreateEvent (
                  EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
                  EFI_TPL_NOTIFY,
                  USBKeyboardRecoveryHandler,
                  UsbKeyboardDevice,
                  &UsbKeyboardDevice->DelayedRecoveryEvent
                  );

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
KeyboardHandler (
  IN  VOID          *Data,
  IN  UINTN         DataLength,
  IN  VOID          *Context,
  IN  UINT32        Result
  )
/*++
  
  Routine Description:
    Handler function for USB Keyboard's asynchronous interrupt transfer.
    
  Arguments:
    Data       A pointer to a buffer that is filled with key data which is
               retrieved via asynchronous interrupt transfer.
    DataLength Indicates the size of the data buffer.
    Context    Pointing to USB_KB_DEV instance.
    Result     Indicates the result of the asynchronous interrupt transfer.
    
  Returns:
    EFI_SUCCESS      - Success
    EFI_DEVICE_ERROR - Hardware Error
--*/  
{
  USB_KB_DEV          *UsbKeyboardDevice;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT8               *CurKeyCodeBuffer;
  UINT8               *OldKeyCodeBuffer;
  UINT8               CurModifierMap;
  UINT8               OldModifierMap;
  UINT8               Index;
  UINT8               Index2;
  BOOLEAN             Down;
  EFI_STATUS          Status;
  BOOLEAN             KeyRelease;
  BOOLEAN             KeyPress;
  UINT8               SavedTail;
  USB_KEY             UsbKey;
  UINT8               NewRepeatKey;
  UINT32              UsbStatus;
  UINT8               *DataPtr;

  ASSERT (Context);

  NewRepeatKey      = 0;
  DataPtr           = (UINT8 *) Data;
  UsbKeyboardDevice = (USB_KB_DEV *) Context;
  UsbIo             = UsbKeyboardDevice->UsbIo;

  //
  // Analyzes the Result and performs corresponding action.
  //
  if (Result != EFI_USB_NOERROR) {
    //
    // Some errors happen during the process
    //
    KbdReportStatusCode (
      UsbKeyboardDevice->DevicePath,
      EFI_ERROR_CODE | EFI_ERROR_MINOR,
      (EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_INPUT_ERROR)
      );

    //
    // stop the repeat key generation if any
    //
    UsbKeyboardDevice->RepeatKey = 0;

    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerCancel,
          USBKBD_REPEAT_RATE
          );

    if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) {
      UsbClearEndpointHalt (
        UsbIo,
        UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress,
        &UsbStatus
        );
    }
    
    //
    // Delete & Submit this interrupt again
    //
    
    Status = UsbIo->UsbAsyncInterruptTransfer (
                      UsbIo,
                      UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress,
                      FALSE,
                      0,
                      0,
                      NULL,
                      NULL
                      );

    gBS->SetTimer (
          UsbKeyboardDevice->DelayedRecoveryEvent,
          TimerRelative,
          EFI_USB_INTERRUPT_DELAY
          );

    return EFI_DEVICE_ERROR;
  }

  if (DataLength == 0 || Data == NULL) {
    return EFI_SUCCESS;
  }

  CurKeyCodeBuffer  = (UINT8 *) Data;
  OldKeyCodeBuffer  = UsbKeyboardDevice->LastKeyCodeArray;

  //
  // checks for new key stroke.
  // if no new key got, return immediately.
  //
  for (Index = 0; Index < 8; Index++) {
    if (OldKeyCodeBuffer[Index] != CurKeyCodeBuffer[Index]) {
      break;
    }
  }

  if (Index == 8) {
    return EFI_SUCCESS;
  }
  
  //
  // Parse the modifier key
  //
  CurModifierMap  = CurKeyCodeBuffer[0];
  OldModifierMap  = OldKeyCodeBuffer[0];

  //
  // handle modifier key's pressing or releasing situation.
  //
  for (Index = 0; Index < 8; Index++) {

    if ((CurModifierMap & KB_Mod[Index].Mask) != (OldModifierMap & KB_Mod[Index].Mask)) {
      //
      // if current modifier key is up, then
      // CurModifierMap & KB_Mod[Index].Mask = 0;
      // otherwize it is a non-zero value.
      // Inserts the pressed modifier key into key buffer.
      //
      Down = (UINT8) (CurModifierMap & KB_Mod[Index].Mask);
      InsertKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), KB_Mod[Index].Key, Down);
    }
  }
  
  //
  // handle normal key's releasing situation
  //
  KeyRelease = FALSE;
  for (Index = 2; Index < 8; Index++) {

    if (!USBKBD_VALID_KEYCODE (OldKeyCodeBuffer[Index])) {
      continue;
    }

    KeyRelease = TRUE;
    for (Index2 = 2; Index2 < 8; Index2++) {

      if (!USBKBD_VALID_KEYCODE (CurKeyCodeBuffer[Index2])) {
        continue;
      }

      if (OldKeyCodeBuffer[Index] == CurKeyCodeBuffer[Index2]) {
        KeyRelease = FALSE;
        break;
      }
    }

    if (KeyRelease) {
      InsertKeyCode (
        &(UsbKeyboardDevice->KeyboardBuffer),
        OldKeyCodeBuffer[Index],
        0
        );
      //
      // the original reapeat key is released.
      //
      if (OldKeyCodeBuffer[Index] == UsbKeyboardDevice->RepeatKey) {
        UsbKeyboardDevice->RepeatKey = 0;
      }
    }
  }
    
  //
  // original repeat key is released, cancel the repeat timer
  //
  if (UsbKeyboardDevice->RepeatKey == 0) {
    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerCancel,
          USBKBD_REPEAT_RATE
          );
  }
  
  //
  // handle normal key's pressing situation
  //
  KeyPress = FALSE;
  for (Index = 2; Index < 8; Index++) {

    if (!USBKBD_VALID_KEYCODE (CurKeyCodeBuffer[Index])) {
      continue;
    }

    KeyPress = TRUE;
    for (Index2 = 2; Index2 < 8; Index2++) {

      if (!USBKBD_VALID_KEYCODE (OldKeyCodeBuffer[Index2])) {
        continue;
      }

      if (CurKeyCodeBuffer[Index] == OldKeyCodeBuffer[Index2]) {
        KeyPress = FALSE;
        break;
      }
    }

    if (KeyPress) {
      InsertKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), CurKeyCodeBuffer[Index], 1);
      //
      // NumLock pressed or CapsLock pressed
      //
      if (CurKeyCodeBuffer[Index] == 0x53 || CurKeyCodeBuffer[Index] == 0x39) {
        UsbKeyboardDevice->RepeatKey = 0;
      } else {
        NewRepeatKey = CurKeyCodeBuffer[Index];
        //
        // do not repeat the original repeated key
        //
        UsbKeyboardDevice->RepeatKey = 0;
      }
    }
  }
  
  //
  // Update LastKeycodeArray[] buffer in the
  // Usb Keyboard Device data structure.
  //
  for (Index = 0; Index < 8; Index++) {
    UsbKeyboardDevice->LastKeyCodeArray[Index] = CurKeyCodeBuffer[Index];
  }
  
  //
  // pre-process KeyboardBuffer, pop out the ctrl,alt,del key in sequence
  // and judge whether it will invoke reset event.
  //
  SavedTail = UsbKeyboardDevice->KeyboardBuffer.bTail;
  Index     = UsbKeyboardDevice->KeyboardBuffer.bHead;
  while (Index != SavedTail) {
    RemoveKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), &UsbKey);

    switch (UsbKey.KeyCode) {

    case 0xe0:
    case 0xe4:
      if (UsbKey.Down) {
        UsbKeyboardDevice->CtrlOn = 1;
      } else {
        UsbKeyboardDevice->CtrlOn = 0;
      }
      break;

    case 0xe2:
    case 0xe6:
      if (UsbKey.Down) {
        UsbKeyboardDevice->AltOn = 1;
      } else {
        UsbKeyboardDevice->AltOn = 0;
      }
      break;

    //
    // Del Key Code
    //
    case 0x4c:
    case 0x63:
      if (UsbKey.Down) {
        if (UsbKeyboardDevice->CtrlOn && UsbKeyboardDevice->AltOn) {
          gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
        }
      }
      break;

    default:
      break;
    }
    
    //
    // insert the key back to the buffer.
    // so the key sequence will not be destroyed.
    //
    InsertKeyCode (
      &(UsbKeyboardDevice->KeyboardBuffer),
      UsbKey.KeyCode,
      UsbKey.Down
      );
    Index = UsbKeyboardDevice->KeyboardBuffer.bHead;

  }
  //
  // If have new key pressed, update the RepeatKey value, and set the
  // timer to repeate delay timer
  //
  if (NewRepeatKey != 0) {
    //
    // sets trigger time to "Repeat Delay Time",
    // to trigger the repeat timer when the key is hold long
    // enough time.
    //
    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerRelative,
          USBKBD_REPEAT_DELAY
          );
    UsbKeyboardDevice->RepeatKey = NewRepeatKey;
  }

  return EFI_SUCCESS;
}

EFI_STATUS
USBParseKey (
  IN OUT  USB_KB_DEV  *UsbKeyboardDevice,
  OUT     UINT8       *KeyChar
  )
/*++
  
  Routine Description:
    Retrieves a key character after parsing the raw data in keyboard buffer.
    
  Arguments:
    UsbKeyboardDevice    The USB_KB_DEV instance.
    KeyChar              Points to the Key character after key parsing.
    
  Returns:
    EFI_SUCCESS   - Success
    EFI_NOT_READY - Device is not ready 
--*/  
{
  USB_KEY UsbKey;

  *KeyChar = 0;

  while (!IsUSBKeyboardBufferEmpty (UsbKeyboardDevice->KeyboardBuffer)) {
    //
    // pops one raw data off.
    //
    RemoveKeyCode (&(UsbKeyboardDevice->KeyboardBuffer), &UsbKey);

    if (!UsbKey.Down) {
      switch (UsbKey.KeyCode) {

      case 0xe0:
      case 0xe4:
        UsbKeyboardDevice->CtrlOn = 0;
        break;

      case 0xe1:
      case 0xe5:
        UsbKeyboardDevice->ShiftOn = 0;
        break;

      case 0xe2:
      case 0xe6:
        UsbKeyboardDevice->AltOn = 0;
        break;

      default:
        break;
      }

      continue;
    }
    
    //
    // Analyzes key pressing situation
    //
    switch (UsbKey.KeyCode) {

    case 0xe0:
    case 0xe4:
      UsbKeyboardDevice->CtrlOn = 1;
      continue;
      break;

    case 0xe1:
    case 0xe5:
      UsbKeyboardDevice->ShiftOn = 1;
      continue;
      break;

    case 0xe2:
    case 0xe6:
      UsbKeyboardDevice->AltOn = 1;
      continue;
      break;

    case 0xe3:
    case 0xe7:
      continue;
      break;

    case 0x53:
      UsbKeyboardDevice->NumLockOn ^= 1;
	  //
	  // Turn on the NumLock light on KB
	  //
      SetKeyLED (UsbKeyboardDevice);
      continue;
      break;

    case 0x39:
      UsbKeyboardDevice->CapsOn ^= 1;
	  //
	  // Turn on the CapsLock light on KB
	  //
      SetKeyLED (UsbKeyboardDevice);
      continue;
      break;
    
    case 0x47:
      UsbKeyboardDevice->ScrollOn ^= 1;
	  //
	  // Turn on the ScrollLock light on KB
	  //
      SetKeyLED (UsbKeyboardDevice);
      continue;
      break;

    //
    // PrintScreen,Pause,Application,Power
    // keys are not valid EFI key
    //

    case 0x46:
    //
    // fall through
    //
    case 0x48:
    //
    // fall through
    //
    case 0x65:
    //
    // fall through
    //
    case 0x66:
    //
    // fall through
    //
      continue;
      break;

    default:
      break;
    }
    
    //
    // When encountered Del Key...
    //
    if (UsbKey.KeyCode == 0x4c || UsbKey.KeyCode == 0x63) {
      if (UsbKeyboardDevice->CtrlOn && UsbKeyboardDevice->AltOn) {
        gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
      }
    }

    *KeyChar = UsbKey.KeyCode;
    return EFI_SUCCESS;
  }

  return EFI_NOT_READY;

}


EFI_STATUS
USBKeyCodeToEFIScanCode (
  IN  USB_KB_DEV      *UsbKeyboardDevice,
  IN  UINT8           KeyChar,
  OUT EFI_INPUT_KEY   *Key
  )
/*++
  
  Routine Description:
    Converts USB Keyboard code to EFI Scan Code.
    
  Arguments:  
    UsbKeyboardDevice    The USB_KB_DEV instance.
    KeyChar              Indicates the key code that will be interpreted.    
    Key                  A pointer to a buffer that is filled in with 
                         the keystroke information for the key that 
                         was pressed.
  Returns:
    EFI_NOT_READY - Device is not ready
    EFI_SUCCESS   - Success
--*/
{
  UINT8 Index;

  if (!USBKBD_VALID_KEYCODE (KeyChar)) {
    return EFI_NOT_READY;
  }
  
  //
  // valid USB Key Code starts from 4
  //
  Index = (UINT8) (KeyChar - 4);

  if (Index >= USB_KEYCODE_MAX_MAKE) {
    return EFI_NOT_READY;
  }

  Key->ScanCode = KeyConvertionTable[Index][0];

  if (UsbKeyboardDevice->ShiftOn) {

    Key->UnicodeChar = KeyConvertionTable[Index][2];

  } else {

    Key->UnicodeChar = KeyConvertionTable[Index][1];
  }

  if (UsbKeyboardDevice->CapsOn) {

    if (Key->UnicodeChar >= 'a' && Key->UnicodeChar <= 'z') {

      Key->UnicodeChar = KeyConvertionTable[Index][2];

    } else if (Key->UnicodeChar >= 'A' && Key->UnicodeChar <= 'Z') {

      Key->UnicodeChar = KeyConvertionTable[Index][1];

    }
  }

  if (KeyChar >= 0x59 && KeyChar <= 0x63) {

    if (UsbKeyboardDevice->NumLockOn && !UsbKeyboardDevice->ShiftOn) {

      Key->ScanCode = SCAN_NULL;

    } else {

      Key->UnicodeChar = 0x00;
    }
  }

  if (Key->UnicodeChar == 0 && Key->ScanCode == SCAN_NULL) {
    return EFI_NOT_READY;
  }

  return EFI_SUCCESS;

}


EFI_STATUS
InitUSBKeyBuffer (
  IN OUT  USB_KB_BUFFER   *KeyboardBuffer
  )
/*++
  
  Routine Description:
    Resets USB Keyboard Buffer.
    
  Arguments:
    KeyboardBuffer - Points to the USB Keyboard Buffer.
    
  Returns:
    EFI_SUCCESS - Success
--*/  
{
  EfiZeroMem (KeyboardBuffer, sizeof (USB_KB_BUFFER));

  KeyboardBuffer->bHead = KeyboardBuffer->bTail;

  return EFI_SUCCESS;
}

BOOLEAN
IsUSBKeyboardBufferEmpty (
  IN  USB_KB_BUFFER   KeyboardBuffer
  )
/*++
  
  Routine Description:
    Check whether USB Keyboard buffer is empty.
    
  Arguments:
    KeyboardBuffer - USB Keyboard Buffer.
    
  Returns:
  
--*/
{
  //
  // meet FIFO empty condition
  //
  return (BOOLEAN) (KeyboardBuffer.bHead == KeyboardBuffer.bTail);
}


BOOLEAN
IsUSBKeyboardBufferFull (
  IN  USB_KB_BUFFER   KeyboardBuffer
  )
/*++
  
  Routine Description:
    Check whether USB Keyboard buffer is full.
    
  Arguments:
    KeyboardBuffer - USB Keyboard Buffer.
    
  Returns:
  
--*/
{
  return (BOOLEAN)(((KeyboardBuffer.bTail + 1) % (MAX_KEY_ALLOWED + 1)) == 
                                                        KeyboardBuffer.bHead);
}


EFI_STATUS
InsertKeyCode (
  IN OUT  USB_KB_BUFFER *KeyboardBuffer,
  IN      UINT8         Key,
  IN      UINT8         Down
  )
/*++
  
  Routine Description:
    Inserts a key code into keyboard buffer.
    
  Arguments:
    KeyboardBuffer - Points to the USB Keyboard Buffer.
    Key            - Key code
    Down           - Special key
  Returns:
    EFI_SUCCESS - Success
--*/
{
  USB_KEY UsbKey;

  //
  // if keyboard buffer is full, throw the
  // first key out of the keyboard buffer.
  //
  if (IsUSBKeyboardBufferFull (*KeyboardBuffer)) {
    RemoveKeyCode (KeyboardBuffer, &UsbKey);
  }

  KeyboardBuffer->buffer[KeyboardBuffer->bTail].KeyCode = Key;
  KeyboardBuffer->buffer[KeyboardBuffer->bTail].Down    = Down;

  //
  // adjust the tail pointer of the FIFO keyboard buffer.
  //
  KeyboardBuffer->bTail = (UINT8) ((KeyboardBuffer->bTail + 1) % (MAX_KEY_ALLOWED + 1));

  return EFI_SUCCESS;
}

EFI_STATUS
RemoveKeyCode (
  IN OUT  USB_KB_BUFFER *KeyboardBuffer,
  OUT     USB_KEY       *UsbKey
  )
/*++
  
  Routine Description:
    Pops a key code off from keyboard buffer.
    
  Arguments:
    KeyboardBuffer -  Points to the USB Keyboard Buffer.
    UsbKey         -  Points to the buffer that contains a usb key code.
  
  Returns:
    EFI_SUCCESS      - Success
    EFI_DEVICE_ERROR - Hardware Error
--*/  
{
  if (IsUSBKeyboardBufferEmpty (*KeyboardBuffer)) {
    return EFI_DEVICE_ERROR;
  }

  UsbKey->KeyCode = KeyboardBuffer->buffer[KeyboardBuffer->bHead].KeyCode;
  UsbKey->Down    = KeyboardBuffer->buffer[KeyboardBuffer->bHead].Down;

  //
  // adjust the head pointer of the FIFO keyboard buffer.
  //
  KeyboardBuffer->bHead = (UINT8) ((KeyboardBuffer->bHead + 1) % (MAX_KEY_ALLOWED + 1));

  return EFI_SUCCESS;
}

EFI_STATUS
SetKeyLED (
  IN  USB_KB_DEV    *UsbKeyboardDevice
  )
/*++
  
  Routine Description:
    Sets USB Keyboard LED state.
    
  Arguments:
    UsbKeyboardDevice - The USB_KB_DEV instance.
  
  Returns:
    EFI_SUCCESS - Success
--*/  
{
  LED_MAP Led;
  UINT8   ReportId;

  //
  // Set each field in Led map.
  //
  Led.NumLock    = (UINT8) UsbKeyboardDevice->NumLockOn;
  Led.CapsLock   = (UINT8) UsbKeyboardDevice->CapsOn;
  Led.ScrollLock = (UINT8) UsbKeyboardDevice->ScrollOn;
  Led.Resrvd     = 0;

  ReportId       = 0;
  //
  // call Set Report Request to lighten the LED.
  //
  UsbSetReportRequest (
    UsbKeyboardDevice->UsbIo,
    UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber,
    ReportId,
    HID_OUTPUT_REPORT,
    1,
    (CHAR8 *) &Led
    );

  return EFI_SUCCESS;
}

VOID
EFIAPI
USBKeyboardRepeatHandler (
  IN    EFI_EVENT    Event,
  IN    VOID         *Context
  )
/*++
  
  Routine Description:
    Timer handler for Repeat Key timer.
    
  Arguments:
    Event   - The Repeat Key event.
    Context - Points to the USB_KB_DEV instance.
    
  Returns:
  
--*/    
{
  USB_KB_DEV  *UsbKeyboardDevice;

  UsbKeyboardDevice = (USB_KB_DEV *) Context;

  //
  // Do nothing when there is no repeat key.
  //
  if (UsbKeyboardDevice->RepeatKey != 0) {
    //
    // Inserts one Repeat key into keyboard buffer,
    //
    InsertKeyCode (
      &(UsbKeyboardDevice->KeyboardBuffer),
      UsbKeyboardDevice->RepeatKey,
      1
      );

    //
    // set repeate rate for repeat key generation.
    //
    gBS->SetTimer (
          UsbKeyboardDevice->RepeatTimer,
          TimerRelative,
          USBKBD_REPEAT_RATE
          );

  }
}

VOID
EFIAPI
USBKeyboardRecoveryHandler (
  IN    EFI_EVENT    Event,
  IN    VOID         *Context
  )
/*++
  
  Routine Description:
    Timer handler for Delayed Recovery timer.
    
  Arguments:
    Event   -  The Delayed Recovery event.
    Context -  Points to the USB_KB_DEV instance.
    
  Returns:
  
--*/    
{

  USB_KB_DEV          *UsbKeyboardDevice;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT8               PacketSize;

  UsbKeyboardDevice = (USB_KB_DEV *) Context;

  UsbIo             = UsbKeyboardDevice->UsbIo;

  PacketSize        = (UINT8) (UsbKeyboardDevice->IntEndpointDescriptor.MaxPacketSize);

  UsbIo->UsbAsyncInterruptTransfer (
          UsbIo,
          UsbKeyboardDevice->IntEndpointDescriptor.EndpointAddress,
          TRUE,
          UsbKeyboardDevice->IntEndpointDescriptor.Interval,
          PacketSize,
          KeyboardHandler,
          UsbKeyboardDevice
          );
}
